#include "gl.hpp"
#include <GLFW/glfw3.h>
#include <iostream>
#include <boost/exception/all.hpp>
#include "glm/gtc/type_ptr.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "Input.hpp"

#include <filesystem>
#if (_MSC_VER == 1900)
namespace filesystem = std::experimental::filesystem;
#else
namespace filesystem = std::filesystem;
#endif

using namespace std;
using namespace boost;

#include "Global.hpp"
#include "Game.hpp"
#include "ConfigFile.hpp"

void main()
{
	try
	{
		ConfigFile Config("Config.cfg");

		int Width=Config.GetValue<int>("XRes");
		int Height=Config.GetValue<int>("YRes");
		bool Fullscreen=Config.GetValue<bool>("Fullscreen");

		//check if the path exists:
		filesystem::path ResourcePath(ResourceDir);

		if(!filesystem::exists(ResourcePath))
		{
			cerr << "Path does not exist: " << filesystem::absolute(ResourcePath) << endl;
		}


		glfwInit();

		if(Fullscreen)
		{
			auto monitor = glfwGetPrimaryMonitor();
			auto default_res = glfwGetVideoMode(monitor);
			Width = default_res->width;
			Height = default_res->height;
		}


		glfwWindowHint(GLFW_VISIBLE, GL_TRUE);
		glfwWindowHint(GLFW_SAMPLES, 8);
		//glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
		//glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
		auto GlfwWindow = glfwCreateWindow(Width, Height, "Water Breakout",
			Fullscreen ? glfwGetPrimaryMonitor() : nullptr, nullptr);

		glfwMakeContextCurrent(GlfwWindow);

		Input::Init(GlfwWindow);

		glViewport(0, 0, (GLsizei)Width, (GLsizei)Height);
		glEnable(GL_CULL_FACE);
		glCullFace(GL_BACK);
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_NORMALIZE);
		glClearColor(0.4f, 0.4f, 0.8f, 1.0f);


		// init OpenGL projection  here
		glMatrixMode(GL_PROJECTION);
		auto look = glm::perspective(45.f/180.f*glm::pi<float>(), (float)Width/Height, 0.1f, 500.0f);
		glLoadMatrixf(glm::value_ptr(look));

		glMatrixMode(GL_MODELVIEW);
		try
		{
			auto game = make_unique<Game>(Config, Width, Height);
			glfwSetWindowUserPointer(GlfwWindow, game.get()); // for callbacks

			game->start();

			auto ResizeCallback = [](GLFWwindow* window, int width, int height)
			{
				if(width!=0 && height!=0) // sometimes window size is 0 (e.g. minimized, i guess)
				{
					glViewport(0, 0, (GLsizei)width, (GLsizei)height);

					glMatrixMode(GL_PROJECTION);
					auto look = glm::perspective(45.f/180.f*glm::pi<float>(), (float)width/height, 0.1f, 500.0f);
					glLoadMatrixf(glm::value_ptr(look));
					glMatrixMode(GL_MODELVIEW);
					auto game = reinterpret_cast<Game*>(glfwGetWindowUserPointer(window));
					game->SetScreenSize(width, height);
				}
			};
			glfwSetFramebufferSizeCallback(GlfwWindow, ResizeCallback);
			cout << " --- Game started --- " << endl;

			double Time=0;
			double LastTime=glfwGetTime();
		
			while(!glfwWindowShouldClose(GlfwWindow))
			{
				Time=glfwGetTime()-LastTime;
				LastTime=glfwGetTime();
				//minimal framerate of 10
				if(Time>0.1f)
					Time=0.1f;

				Input::Update(); // also calls PollEvents


				if(game->onUpdate(Time, GlfwWindow))
				{
					game = nullptr; // explicitly destroy game before closing window
					glfwDestroyWindow(GlfwWindow);
					break; // exit the loop immediately
				}


				//camera mode switch:
				if(Input::GetKeyClicked(Input::KEY_1))
					game->cameraMode=0;
				if(Input::GetKeyClicked(Input::KEY_2))
					game->cameraMode=1;
				if(Input::GetKeyClicked(Input::KEY_3))
					game->cameraMode=2;


				glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
				glLoadIdentity();
		
				/*if(m_Wireframe)
					glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
				else
					glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);*/

				game->DrawScene();

				glfwSwapBuffers(GlfwWindow);
			}
		}
		catch(std::exception &ex)
		{
			glfwDestroyWindow(GlfwWindow);
			cout << diagnostic_information(ex) << endl;
			::system("pause");
		}

		glfwTerminate();
	}
	catch(std::exception &ex)
	{
		cout << diagnostic_information(ex) << endl;
		::system("pause");
	}
}